package tuvienna.jade;



import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/*Used in different forms
 * 
 * - for ParticipantAgents their InterestSet starts at 0 for some interests
 *    and (slowly) go to 1 through discussing
 * 
 * - for a Table the interests are a density(probability) function describing
 *    how much each interest is talked about at the table/discussion
 *    
 * - as an intermediate form the inverse participant interests are used to change the discussion
 *    on a table toward the desired one   
 */
public class InterestSet extends HashMap<String,BigDecimal> implements Serializable{
	private static final long serialVersionUID = -4590182135146434457L;

	public InterestSet(){super();}
	public InterestSet(Object[] interests)
	{ 
		super();
		for(int i=0;i<interests.length; i++)
			put(interests[i].toString(), new BigDecimal(0));		
	}
	public InterestSet(Map<String,BigDecimal> clone)
	{
		super();
		for(String i: clone.keySet())
			put(i, clone.get(i));
	}
	
	public static InterestSet getCommonInterests(Collection<InterestSet> interestSets) {
		InterestSet commonInterests = new InterestSet();
		// count number of interests
		for (InterestSet interestSet : interestSets) {
			for (String interest: interestSet.keySet() ) {
				BigDecimal interestCount = commonInterests.get(interest);
				if (interestCount == null) {
					commonInterests.put(interest, BigDecimal.ONE);
				} else {
					commonInterests.put(interest, interestCount.add(BigDecimal.ONE));
				}
			}
		}

		// remove interests with just one participant
		Set<String> loneInterests = new HashSet<String>();
		for (String interest:commonInterests.keySet() ) {
			if (commonInterests.get(interest).compareTo(BigDecimal.ONE) <=0) {
				loneInterests.add(interest);
			}
		}
		for (String loneInterest: loneInterests) {
			commonInterests.remove(loneInterest);
		}
		
		// return normalized values
		return commonInterests.normalized();
	}
	
	//Density function over "talked" topic, zero if alone at table ;)
	//norm( sum(interests) )
	// does not work
	@Deprecated
	public static InterestSet computeTopic(Collection<InterestSet> other)
	{
		HashMap<String,Boolean> interestTwiceOrMore = new HashMap<String,Boolean>();
		for (InterestSet iset : other)
			for(String key : iset.keySet())
				interestTwiceOrMore.put(key, interestTwiceOrMore.containsKey(key));
		InterestSet res = new InterestSet();
		for (InterestSet iset : other)
		{
			iset = iset.getDesiredTopic();
			for(String i : iset.keySet())
				if(interestTwiceOrMore.get(i))
					if(res.containsKey(i))
						res.put(i, res.get(i).add(iset.get(i)));
					else
						res.put(i, iset.get(i));
		}	
		return res.normalized();
	}
	
	
	
	//Density function over what these Interests wants to talk about 
	//(ie. which interests are least satisfied have highest value)
	// norm(1.0 - this)
	public InterestSet getDesiredTopic()
	{
		InterestSet res = new InterestSet();
		for(String v: keySet())
			res.put(v, BigDecimal.ONE.subtract(get(v)));
		
		return res.normalized();
	}
	
	
	
	//expecting a density function, duration > 0
	// I_can_get_fed_up_with_a_topic(what_I_heard_so_far + what_I_wanted_to_hear * topic_I_heard * how_long_I_did_it)
	//then returns how much my interests got satisfied
	// min(1.0, this + (1.0 - this) * topic * duration)
	public BigDecimal addSatisfaction(InterestSet topic, BigDecimal duration)
	{
		BigDecimal sat = sum();
		for(String v: keySet())
			if(topic.containsKey(v))
				put(v, get(v).add(BigDecimal.ONE.subtract(get(v)).multiply(topic.get(v).multiply(duration))).min(BigDecimal.ONE));	
		return sum().subtract(sat);
	}
	
	/** Returns current InterestSet with values, but only of present in filterSet
	 */
	public InterestSet filter(InterestSet filterSet) {
		InterestSet result = new InterestSet();
		for(String bi : keySet()) {
			if(filterSet.containsKey(bi)) {
				result.put(bi, get(bi));
			}
		}
		return result;
	}
	
	/** 
	 * Add (matching) topic satisaction values to current satisafction values
	 */
	public void addSatisfaction(InterestSet topic) {
		for(String bi : topic.keySet()) {
			if(containsKey(bi)) {
				put(bi, get(bi).add(topic.get(bi)));
			}
		}
	}
	
	public BigDecimal sum()
	{
		BigDecimal sum = new BigDecimal(0);
		for(BigDecimal v: values())
			sum = sum.add(v);//.multiply(v));
		return sum;		
	}
	
	public InterestSet normalized()
	{
		BigDecimal sum = sum();
		InterestSet res = new InterestSet();
		if(sum.doubleValue() > 0)
			for(String i: keySet())
				res.put(i,get(i).divide(sum,MathContext.DECIMAL64));	
		return res;
	}
		
	public void plus(Map<String, BigDecimal> b)
	{
		for(String bi : b.keySet())
			if(containsKey(bi))
				put(bi, get(bi).add(b.get(bi)));
			else
				put(bi, b.get(bi));
	}
	
	public void minus(Map<String, BigDecimal> b)
	{
		for(String bi : b.keySet())
			if(containsKey(bi))
				put(bi, get(bi).subtract(b.get(bi)));
			else
				put(bi, b.get(bi).negate());
	}
	
	public void multiply(Map<String, BigDecimal> b)
	{
		for(String bi : b.keySet())
			put(bi, get(bi).multiply(b.get(bi)));
	}
	
	public void print(String environment) {
		System.out.print("InterestSet("+environment+"): ");
		for(String interest : keySet()) {
			System.out.print(interest+":"+get(interest).setScale(2, BigDecimal.ROUND_HALF_EVEN)+"\t");
		}
		System.out.println();
	}
}
